#include "PCB.H"
#include "R3.h"
#include "MPX_SUPT.H"
#include "ERR_HAND.h"
#include <string.h>
#include <dos.h>

/*Alllocates and sets up a new PCB.  The program for this PCB is read from a given filename,
 *given dynamically-allocated memory, and then loaded into this memory area.
 *NOTE: attempts to create process with same name as file, so such a process must not alrady exist
 *parameters:	filename, char[] - name of the file where the program is found
 *				priority, int - parsed from user input for desired priority of new process
 *returns: void
 *functions called:
 */
void load_program(char filename[], int priority) {
	//error messages if invalid args, prog not found or invalid, PCB or prog mem not allocated, used proc name
	//be sure to initialize context properly:
		//CS and IP derrived from exec address, which is far pointer, using FP_SEG and FP_OFF
		//DS and ES to current values in MPX
		//Flags
	
	pcb *new_pcb;
	context *cp;
	int start_offset;
	int prog_mem_len;
	char name_with_ext[PROC_NAME_LEN+5];
	int prog_check_err;
	int prog_load_err;
	
	//check args
		//valid priority
	if ((priority > 127) || (priority < -128)) {
		err_hand(ERR_R4_INV_PRIOR);
	}
		//unused name
	else if (check_name(filename) == 1) {//changed so that cop isn't found//else if (find_pcb(filename) != NULL) {
		err_hand(ERR_R4_USED_NAME);
	}
	else if (((int)strlen(filename)) > PROC_NAME_LEN) {
		err_hand(ERR_R4_LONG_NAME);
	}
	else {
	
	//add .MPX to filename into string name_with_ext
		strcpy(name_with_ext, filename);
		//strcat(name_with_ext, ".mpx");
		
	//check program
		//uses sys_check_program to check if file exists and what size is required
		prog_check_err = sys_check_program("\0", name_with_ext, &prog_mem_len, &start_offset);
		//check this return value
		if (prog_check_err != 0) {
			if (prog_check_err == ERR_SUP_NAMLNG)
				err_hand(ERR_R4_LONG_FNAME);
			else if (prog_check_err == ERR_SUP_FILNFD)
				err_hand(ERR_R4_NO_FILE);
			else
				err_hand(ERR_R4_INV_FILE);
		}
		else {
	
	//call allocate and setup
		//make all with class application (PROC_CLASS_APP from PCB.H)
		//make all with state suspended-ready
			new_pcb = setup_PCB(filename, PROC_CLASS_APP, priority);
			new_pcb->suspended = PROC_SUSP;
		
		//set PCB mem_size and mem_exec_address based on return value of sys_check_program above
			new_pcb->mem_size = prog_mem_len;
			
	//allocate memory for new program
		//uses sys_alloc_mem. set PCB mem_load_address to the return value from this call
			new_pcb->mem_load_address = (unsigned char*) sys_alloc_mem(new_pcb->mem_size);
			
			//exec address is computed from load address and mem_size from sys_check_program
			new_pcb->mem_exec_address = new_pcb->mem_load_address + start_offset;
			
	//create context pointer for new PCB
			cp = (context*)new_pcb->stack_top;
			cp->IP = FP_OFF(new_pcb->mem_exec_address);
			cp->CS = FP_SEG(new_pcb->mem_exec_address);
			cp->DS = _DS;
			cp->ES = _ES;
			cp->FLAGS = 0x200;
			
	//load program into memory
		//uses sys_load_program
			prog_load_err = sys_load_program(new_pcb->mem_load_address, new_pcb->mem_size, "\0", name_with_ext);
		//check this value
			if (prog_load_err != 0) {
				if (prog_check_err == ERR_SUP_NAMLNG)
					err_hand(ERR_R4_LONG_FNAME);
				else if (prog_check_err == ERR_SUP_FILNFD)
					err_hand(ERR_R4_NO_FILE);
				else if (prog_check_err == ERR_SUP_FILINV)
					err_hand(ERR_R4_INV_FILE);
				else if (prog_check_err == ERR_SUP_PROGSZ)
					err_hand(ERR_R4_PROG_SIZE);
				else if (prog_check_err == ERR_SUP_LDADDR)
					err_hand(ERR_R4_INV_LADD);
				else
					err_hand(ERR_R4_LOAD_FAIL);
			}
			else {
			
	//insert pcb into queue if no error loading
				insert_pcb(new_pcb, new_pcb->state, new_pcb->suspended);
			}
		}
	}
}

/*Deallocates memory for a given process as well as the accompanying program.
 *parameters:	proc_name, char[] - the name of the process to terminate
 *returns: void
 *functions called:
 */
void terminate_process(char name[]) {
	pcb *to_kill;
	to_kill = find_pcb(name);
	
	//check name arg
	if(to_kill == NULL){
	//print error message if process does not exist
		err_hand(ERR_R4_NO_PCB);
	}
	else{
		//do not allow PROC_CLASS_SYS processes to be terminated
		if(to_kill->type == PROC_CLASS_SYS){
			err_hand(ERR_R4_TYPE_SYS);
		}
		else if(to_kill->type == PROC_CLASS_APP){
			delete_pcb(to_kill->name, "\0");
		}
		else{
			err_hand(1);
		}
	}
		
	//deallocate PCB memory as in Delete PCB and deallocate program memory
}

/*Replaces dispatch command for R3.  Dispatches all 
 *parameters: none
 *returns: void
 *functions called:	dispatch, sys_call
 */
void dispatch_round_robin() {
	//takes over dispatch from R3 - very similar except do not setup
		//setup done as program is loaded
		
	//should be able to do most of the work just by calling sys_call and dispatch
}



void move(int x, int y){
	char buf[80];
	int asize;
	int err;
	buf[0] = x;
	buf[1] = y;
	asize = 2;
	
	err = sys_req(GOTOXY, TERMINAL, buf, &asize);
	//sleep(1);
	if(err != 0){
		printf("OH, NO! Error #%d\n", err);
	}
}